package com.bgds.sys.api.service.impl;

import cn.hutool.core.thread.ThreadUtil;
import com.alibaba.fastjson.JSON;
import com.bgds.sys.api.factory.MsgKeyFactory;
import com.bgds.sys.api.feign.BaoXinClient;
import com.bgds.sys.api.service.BaoXinService;
import com.bgds.sys.common.constant.BizConstant;
import com.bgds.sys.common.utils.ArithUtils;
import com.bgds.sys.common.utils.DateUtils;
import com.bgds.sys.data.iXCom29d.gateway.impl.BaoXingVo;
import com.bgds.sys.data.iXCom29d.protocol.utils.IXComActionParamDTO;
import com.bgds.sys.data.iXCom29d.protocol.utils.IXComCodecService;
import com.bgds.sys.influx.utils.InfluxUtils;
import com.bgds.sys.module.entity.DeviceDynamicAttr;
import com.bgds.sys.module.entity.DeviceFault;
import com.bgds.sys.module.entity.FunctionInputProject;
import com.bgds.sys.module.entity.dto.DeviceFaultDto;
import com.bgds.sys.module.entity.dto.DeviceFaultResponese;
import com.bgds.sys.module.entity.dto.PropertyConfigDto;
import com.bgds.sys.module.mapper.DeviceDynamicAttrMapper;
import com.bgds.sys.module.service.IAlarmLogService;
import com.bgds.sys.module.service.IDeviceDynamicAttrService;
import com.bgds.sys.module.service.IFunctionInputProjectService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * 宝信数据接收服务类
 *
 * @author lj
 */
@Component
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class BaoXinServiceImpl implements BaoXinService {

    @Autowired
    private DeviceDynamicAttrMapper deviceDynamicAttrMapper;
    @Autowired
    private IDeviceDynamicAttrService iDeviceDynamicAttrService;
    @Autowired
    private IXComCodecService ixComCodecService;
    @Autowired
    private InfluxUtils influxUtils;
    @Autowired
    private IAlarmLogService iAlarmLogService;

    @Autowired
    private IFunctionInputProjectService iFunctionInputProjectService;

    private final ThreadPoolExecutor threadPoolExecutor = ThreadUtil.newExecutor(5, 30);

    /**
     * 接收宝信Socket消息，并对应MySQL数据库监测点解析
     *
     * @param vo
     */
    @Override
    public void receiveMessage(BaoXingVo vo) {
        String deviceCode = vo.getMsgKey();
        List<PropertyConfigDto> proPertyConfigDtos = deviceDynamicAttrMapper.queryByDeviceCode(deviceCode);
        IXComActionParamDTO ixComActionParamDTO = new IXComActionParamDTO();
        List<IXComActionParamDTO.PropertyConfig> propertyConfigs = new ArrayList<>();
        for (PropertyConfigDto pcd : proPertyConfigDtos) {
            IXComActionParamDTO.PropertyConfig propertyConfig = new IXComActionParamDTO.PropertyConfig();
            if ("FLOAT".equalsIgnoreCase(pcd.getPropertyType())) {
                propertyConfig.setPropertyType(IXComActionParamDTO.PropertyType.FLOAT);
            } else if ("int".equalsIgnoreCase(pcd.getPropertyType())) {
                propertyConfig.setPropertyType(IXComActionParamDTO.PropertyType.INT);
            }
            propertyConfig.setFixedLength(pcd.getFixedLength());
            propertyConfig.setPrecision(pcd.getPrecision());
            propertyConfig.setPropertyId(pcd.getPropertyId());
            propertyConfigs.add(propertyConfig);
        }
        ixComActionParamDTO.setPropertyList(propertyConfigs);
        Map<String, Object> map = ixComCodecService.decode(ixComActionParamDTO, vo.getData(), Charset.forName("GBK"));
        //存入influxDB
        Map<String, Object> fields = new HashMap<>(10);
        Iterator<Map.Entry<String, Object>> entries = map.entrySet().iterator();
        //随机小数
//        Double s= ArithUtils.rd();
//        BigDecimal bg = new BigDecimal(String.valueOf(s));
//        Float d = bg.setScale(2, BigDecimal.ROUND_HALF_UP).floatValue();

        //模拟数据：测试数据+随机数


        while (entries.hasNext()) {
            Map.Entry<String, Object> entry = entries.next();
            String key = entry.getKey();
            Float f = Float.parseFloat(String.valueOf(entry.getValue()));
            Integer rd = new Random().nextInt(3) % (3 - 1 + 1) + 1;
            Float f1 = Float.parseFloat(String.valueOf(rd));
            Float value1 = Float.sum(f, f1);
            Double value = ArithUtils.round(value1, 2);

//            Float f1=Float.parseFloat(String.valueOf(d));
//            Float value1 =Float.sum(f,f1);
//            Double value=ArithUtils.round(value1,2);
            fields.put(key, String.valueOf(value));
        }
        influxUtils.insert(deviceCode, null, fields);
        //iAlarmLogService.insert(proPertyConfigDtos,fields);
    }

    @Override
    public void analysisMessage(BaoXingVo vo) {
        //电文号
        String msgKey = vo.getMsgKey();
        //获取电文号包含的数据项
        List<PropertyConfigDto> proPertyConfigDtos = deviceDynamicAttrMapper.queryByMsgKey(msgKey);

        IXComActionParamDTO ixComActionParamDTO = new IXComActionParamDTO();
        List<IXComActionParamDTO.PropertyConfig> propertyConfigs = new ArrayList<>();
        //数据项按电文序号排序
        List<PropertyConfigDto> sorts = proPertyConfigDtos.stream().sorted(Comparator.comparing(PropertyConfigDto::getSeqNumber)).collect(Collectors.toList());

        for (PropertyConfigDto pcd : sorts) {
            IXComActionParamDTO.PropertyConfig propertyConfig = new IXComActionParamDTO.PropertyConfig();
            if ("FLOAT".equalsIgnoreCase(pcd.getPropertyType())) {
                propertyConfig.setPropertyType(IXComActionParamDTO.PropertyType.FLOAT);
            } else if ("int".equalsIgnoreCase(pcd.getPropertyType())) {
                propertyConfig.setPropertyType(IXComActionParamDTO.PropertyType.INT);
            } else if ("bit".equalsIgnoreCase(pcd.getPropertyType())) {
                propertyConfig.setPropertyType(IXComActionParamDTO.PropertyType.BYTE);
            }
            propertyConfig.setId(pcd.getId());
            propertyConfig.setFixedLength(pcd.getFixedLength());
            propertyConfig.setPrecision(pcd.getPrecision());
            propertyConfig.setPropertyId(pcd.getPropertyId());
            propertyConfig.setConvertTo(pcd.getConvertTo());
            propertyConfig.setGroupCode(pcd.getGroupCode());
            propertyConfig.setAlarmFlag(pcd.getAlarmFlag());
            propertyConfig.setAlarmFilter(pcd.getAlarmFilter());
            propertyConfig.setName(pcd.getName());
            //设备编码作为measurement
            propertyConfig.setMeasurementName(pcd.getDeviceCode());
            propertyConfigs.add(propertyConfig);
        }
        ixComActionParamDTO.setPropertyList(propertyConfigs);
        List<IXComActionParamDTO.PropertyConfig> propertyConfigList = ixComCodecService.decode1(ixComActionParamDTO, vo.getData(), Charset.forName("GBK"));
        Map<String, List<IXComActionParamDTO.PropertyConfig>> groupMap = propertyConfigList.stream().collect(
                Collectors.groupingBy((IXComActionParamDTO.PropertyConfig pcd) -> pcd.getMeasurementName())
        );
        //存值到influxDB数据库-拆分成influxDB多表保存
        groupMap.forEach((measurement, configs) -> {
            Map<String, Object> fields = new HashMap<>(10);
            for (IXComActionParamDTO.PropertyConfig pcf : configs) {
                fields.put(pcf.getPropertyId(), pcf.getValue());
            }
            if (StringUtils.isNotBlank(measurement)) {
                influxUtils.insert(measurement, null, fields);
            }
        });

        if (MsgKeyFactory.getIsSync(msgKey, System.currentTimeMillis())) {
            //保存动态属性现值和差值到mysql数据库
            threadPoolExecutor.execute(() -> {
                SaveDeviceDynamicAttrsToMysql(propertyConfigList);
            });
        }

    }

    /**
     * 保存动态属性现值和差值到mysql数据库
     */
    private void SaveDeviceDynamicAttrsToMysql(List<IXComActionParamDTO.PropertyConfig> propertyConfigList) {
        //普通的动态属性保存集合
        List<DeviceDynamicAttr> deviceDynamicAttrList = new ArrayList();
        propertyConfigList.forEach(pc -> {
            DeviceDynamicAttr deviceDynamicAttr = new DeviceDynamicAttr();
            deviceDynamicAttr.setId(pc.getId());
            deviceDynamicAttr.setDeviceCode(pc.getMeasurementName());
            deviceDynamicAttr.setEngName(pc.getPropertyId());
            deviceDynamicAttr.setGroupCode(pc.getGroupCode());
            deviceDynamicAttr.setAlarmFlag(pc.getAlarmFlag());
            deviceDynamicAttr.setAlarmFilter(pc.getAlarmFilter());
            deviceDynamicAttr.setName(pc.getName());
            deviceDynamicAttr.setCurrentValue(Double.valueOf(pc.getValue().toString()));
            deviceDynamicAttrList.add(deviceDynamicAttr);
        });

        //查出需要计算差值的动态属性
        List<DeviceDynamicAttr> calcuList = deviceDynamicAttrList.stream().filter(p -> BizConstant.ALARM_FILTER_SET.equalsIgnoreCase(p.getAlarmFilter())
                || BizConstant.ALARM_FILTER_FEEDBACK.equalsIgnoreCase(p.getAlarmFilter()))
                .sorted(Comparator.comparing(DeviceDynamicAttr::getEngName))
                .collect(Collectors.toList());
        Map<String, List<DeviceDynamicAttr>> map = calcuList.stream().collect(Collectors.groupingBy(DeviceDynamicAttr::getDeviceCode));
        //需要更新现值动态属性集合
        List<DeviceDynamicAttr> deviceDynamicAttrUpDateTotalList = new ArrayList();
        //把需要报警的报文过滤出来,需要报警才更新现值,更新到mysql数据库
        List<DeviceDynamicAttr> deviceDynamicAttrFilterUpdate = deviceDynamicAttrList.stream().filter(DeviceDynamicAttr::getAlarmFlag).collect(Collectors.toList());
        deviceDynamicAttrUpDateTotalList.addAll(deviceDynamicAttrFilterUpdate);

        //先加入差值动态属性
        List<DeviceDynamicAttr> gapList = new ArrayList<>();
        map.keySet().forEach(deviceCode -> {
            gapList.addAll(iDeviceDynamicAttrService.listAlarmDeviceByDeviceCodeAndAlarmFilter(deviceCode, BizConstant.ALARM_FILTER_GAP));
        });
        //给差值动态属性设置值
        gapList.forEach(gap -> {
            String gapName = gap.getName();
            String gapNamePrefix = gapName.split(BizConstant.GAP)[0];
            List<DeviceDynamicAttr> gapValues = map.get(gap.getDeviceCode());
            if (gapValues.size() < 2) {
                log.info(gap.getDeviceCode() + "," + gapName + "的设定和反馈配置少于1对");
                return;
            }
            DeviceDynamicAttr set = null;
            DeviceDynamicAttr feedback = null;
            for (DeviceDynamicAttr gapValue : gapValues) {
                if (gapValue.getName().contains(gapNamePrefix) && BizConstant.ALARM_FILTER_SET.equalsIgnoreCase(gapValue.getAlarmFilter())) {
                    set = gapValue;
                }
                if (gapValue.getName().contains(gapNamePrefix) && BizConstant.ALARM_FILTER_FEEDBACK.equalsIgnoreCase(gapValue.getAlarmFilter())) {
                    feedback = gapValue;
                }

                if (null != set && feedback != null) {
                    gap.setCurrentValue(set.getCurrentValue() - feedback.getCurrentValue());
                }
            }
            log.info("【报文差值属性设置现值】{}，{}，{}", gap.getDeviceCode(), gap.getEngName(), gap.getCurrentValue());
        });
        //加入差值更新
        deviceDynamicAttrUpDateTotalList.addAll(gapList);


        //更新到mysql数据库
        if (!deviceDynamicAttrUpDateTotalList.isEmpty()) {
            iDeviceDynamicAttrService.updateBatchById(deviceDynamicAttrUpDateTotalList);
            // log.info("【报文更新现值】"+deviceDynamicAttrUpDateTotalList);
        }

    }

    @Override
    public List<DeviceFault> getDeviceFaultData() {
        List<DeviceFault> deviceFaults = new ArrayList<>();
        try {
            String result = BaoXinClient.doPost();
            DeviceFaultResponese responese = JSON.parseObject(result, DeviceFaultResponese.class);
            if (responese != null) {
                for (DeviceFaultDto dto : responese.getList1()) {
                    FunctionInputProject project = iFunctionInputProjectService.getById(dto.getSeqId());
                    DeviceFault fault = new DeviceFault();
                    fault.setFaultContent(project.getRegionCode().concat(project.getProjectName()));
                    Date start = DateUtils.stringConvertDate("yyyy-MM-dd HH:mm:ss", dto.getFaultStartTime());
                    fault.setFaultEndTime(start.getTime());
                    Date end = DateUtils.stringConvertDate("yyyy-MM-dd HH:mm:ss", dto.getFaultEndTime());
                    fault.setFaultEndTime(end.getTime());
                    deviceFaults.add(fault);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return deviceFaults;
    }
}
